"""
认证中间件模块

该模块提供基于JWT令牌的用户认证和基于角色的访问控制功能。
主要功能包括：
1. JWT令牌验证 - 验证请求中的Bearer令牌有效性
2. 用户身份验证 - 验证用户是否存在且状态正常
3. 角色权限控制 - 基于用户角色进行访问控制
4. 端点权限控制 - 基于具体端点和HTTP方法的细粒度权限控制
5. 统一错误处理 - 返回标准化的错误响应

使用示例：
    @auth_required(['admin'])  # 需要admin角色
    async def admin_endpoint(request):
        return Response("Admin access granted")

    @auth_required()  # 只需要有效登录
    async def user_endpoint(request):
        return Response("User access granted")
"""

from robyn import Request, Response
from utils.jwt_util import verify_access_token
from constants import HttpStatus
from functools import wraps
from services import user_service, role_service, permission_service  # 权限服务用于端点级别的权限控制


def _match_path(template_path: str, actual_path: str) -> bool:
    """
    比较模板路径和实际路径是否匹配，支持路径参数。
    例如: /api/users/{user_id} 会匹配 /api/users/123
    """
    template_parts = template_path.strip('/').split('/')
    actual_parts = actual_path.strip('/').split('/')

    if len(template_parts) != len(actual_parts):
        return False

    for template_part, actual_part in zip(template_parts, actual_parts):
        if template_part.startswith('{') and template_part.endswith('}'):
            continue  # This is a path parameter, so it matches.
        if template_part != actual_part:
            return False
    
    return True

def _has_permission(current_endpoint: str, current_method: str, user_permissions: list) -> bool:
    """
    检查用户是否拥有访问特定端点和方法的权限。
    """
    for endpoint_template, method in user_permissions:
        if current_method == method and _match_path(endpoint_template, current_endpoint):
            return True
    return False


def auth_required(required_roles: list = None):
    """
    认证装饰器 - 用于保护需要身份验证和权限控制的API端点
    
    该装饰器实现了完整的认证和授权流程：
    1. 验证Authorization请求头是否存在
    2. 验证Bearer令牌格式和有效性
    3. 验证用户身份和状态
    4. 验证用户角色权限（如果指定了required_roles）
    5. 验证端点级别的权限（基于用户角色对应的权限配置）
    
    参数:
        required_roles (list, optional): 允许访问的角色列表，如果为None则只验证用户身份
        
    返回:
        function: 装饰器函数
        
    功能特点:
        - 支持角色预验证：装饰器创建时会检查所需角色是否存在于数据库
        - 多层次权限控制：同时支持角色级别和端点级别的权限验证
        - 标准化错误响应：所有认证失败都会返回统一的JSON格式错误信息
        - 用户信息传递：认证成功后会将用户信息添加到request.user_info中
        
    错误处理:
        - 401 Unauthorized: 令牌无效、用户不存在、认证信息缺失
        - 403 Forbidden: 角色权限不足、端点权限不足
    """
    def decorator(func):
        # 预验证：检查所需角色是否存在于数据库中
        if required_roles:
            all_db_roles = role_service.get_all_role_names()
            for role in required_roles:
                if role not in all_db_roles:
                    raise ValueError(f"Required role '{role}' does not exist in the database.")

        @wraps(func)
        async def wrapper(request: Request, *args, **kwargs):
            """
            包装函数 - 实现具体的认证和授权逻辑
            
            认证流程（7个步骤）：
            1. 验证Authorization请求头是否存在
            2. 验证Bearer令牌格式
            3. 验证JWT令牌有效性
            4. 提取并验证用户ID
            5. 验证用户是否存在
            6. 验证角色权限（如果指定了required_roles）
            7. 验证端点级别的权限
            """
            # 步骤1: 验证Authorization请求头是否存在
            auth_header = request.headers.get("authorization")
            if not auth_header:
                return Response(
                    status_code=HttpStatus.UNAUTHORIZED,
                    description='{"code": 401, "msg": "Authorization header missing", "data": null}',
                    headers={"Content-Type": "application/json"}
                )

            # 步骤2: 验证Bearer令牌格式
            try:
                token_type, token = auth_header.split(" ", 1)
                if token_type.lower() != "bearer":
                    raise ValueError("Invalid token type")
            except ValueError:
                return Response(
                    status_code=HttpStatus.UNAUTHORIZED,
                    description='{"code": 401, "msg": "Invalid token format", "data": null}',
                    headers={"Content-Type": "application/json"}
                )

            # 步骤3: 验证JWT令牌有效性
            payload = verify_access_token(token)
            if not payload:
                return Response(
                    status_code=HttpStatus.UNAUTHORIZED,
                    description='{"code": 401, "msg": "Invalid or expired token", "data": null}',
                    headers={"Content-Type": "application/json"}
                )

            # 步骤4: 提取并验证用户ID
            user_id = payload.get("user_id")
            if not user_id:
                return Response(
                    status_code=HttpStatus.UNAUTHORIZED,
                    description='{"code": 401, "msg": "User ID not in token", "data": null}',
                    headers={"Content-Type": "application/json"}
                )
            
            # 步骤5: 验证用户是否存在
            user = user_service.getUser(user_id)
            if not user:
                return Response(
                    status_code=HttpStatus.UNAUTHORIZED,
                    description='{"code": 401, "msg": "User not found", "data": null}',
                    headers={"Content-Type": "application/json"}
                )

            # 步骤6: 基于角色的访问控制（RBAC）
            # if required_roles:
            #     user_roles = {role['name'] for role in user.get('roles', [])}
            #     # 检查用户是否拥有所需角色中的任意一个
            #     if not set(required_roles).intersection(user_roles):
            #         return Response(
            #             status_code=HttpStatus.FORBIDDEN,
            #             description='{"code": 403, "msg": "Forbidden", "data": null}',
            #             headers={"Content-Type": "application/json"}
            #         )

            # 步骤7: 基于端点和HTTP方法的细粒度权限控制
            current_endpoint = request.url.path  # 当前请求的API端点路径
            current_method = request.method  # 当前请求的HTTP方法（GET、POST、PUT、DELETE等）



            # 收集用户所有角色对应的权限
            user_permissions = user_service.get_user_permissions(user_id)
            
            # 验证用户是否对当前端点和方法有访问权限
            if not _has_permission(current_endpoint, current_method, user_permissions):
                return Response(
                    status_code=HttpStatus.FORBIDDEN,
                    description='{"code": 403, "msg": "Forbidden: Insufficient permissions", "data": null}',
                    headers={"Content-Type": "application/json"}
                )

            # 认证和授权全部通过，将用户信息传递给被装饰的函数
            # 这样被装饰的函数就可以通过 request.user_info 获取当前登录用户的信息
            request.form_data = payload.get("user_info")
            return await func(request, *args, **kwargs)
        return wrapper
    return decorator